package cn.qingfeng.demo.transaction.springboot.declarative.transactionNest.service.impl;

import cn.qingfeng.demo.transaction.springboot.common.entity.User;
import cn.qingfeng.demo.transaction.springboot.common.mapper.UserMapper;
import cn.qingfeng.demo.transaction.springboot.declarative.transactionNest.service.TransactionNestInnerService;
import cn.qingfeng.demo.transaction.springboot.declarative.transactionNest.service.TransactionNestOutterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.util.UUID;

@Service
//@Transactional//可以用在类上或方法上，都用时以方法上的为准
public class TransactionNestOutterServiceImpl implements TransactionNestOutterService {//这里不要忘记实现接口，否则controller中注入不了。
    //注入用户Mapper
    @Resource(name = "userMapperTransaction")
    private UserMapper userMapper;

    @Autowired
    private TransactionNestInnerService transactionNestInnerService;

    /**
     * 测试PROPAGATION_REQUIRED（默认）
     */
    @Transactional
    @Override
    public void testRequired() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testRequired();

//        System.out.println(1/0);//这里出异常的话，内层外层一起回滚

    }

    @Transactional
    @Override
    public void testRequired2() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        //这里阻止异常上抛无效，只要内层事务回滚了，外层事务就能监听到并一起回滚。
        try {
            transactionNestInnerService.testRequired2();
        } catch (RuntimeException e) {
            e.printStackTrace();
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚事务
        }

        System.out.println(1 / 0);//仅这里出异常的话，内层外层一起回滚

    }

    @Transactional
    @Override
    public void testSupports() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testSupports();

//        System.out.println(1/0);//这里出异常的话，内层外层一起回滚
    }

    @Transactional
    @Override
    public void testMandatory() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testMandatory();

//        System.out.println(1/0);//这里出异常的话，内层外层一起回滚
    }

    /**
     * 【错误的写法，这种写法跟调用一个Propagation.REQUIRED效果是一样的，nested的效果并没有发挥出来】
     */
    @Transactional
    @Override
    public void testNested() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testNested();

//        System.out.println(1 / 0);//这里出异常的话，内层外层一起回滚
    }

    /**
     * 【错误的写法】
     */
    @Transactional
    @Override
    public void testNested2() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testNested2();

//        System.out.println(1 / 0);//仅这里出异常的话，内层外层一起回滚
    }

    /**
     * 【正确的写法】
     */
    @Transactional
    @Override
    public void testNested3() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        //捕获异常，阻止异常上抛。外层事务不会监听nested机制的内层事务的回滚。如果内部回滚触发了外部回滚那是异常上抛引起的，不是内部回滚引起的。
        try {
            transactionNestInnerService.testNested3();
        } catch (RuntimeException e) {
            e.printStackTrace();
        }

//        System.out.println(1 / 0);//仅这里出异常的话，内层外层一起回滚
    }

    /**
     * 【错误的写法】【特殊情况下或可一用】
     */
    @Transactional
    @Override
    public void testRequiredNew() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testRequiredNew();

        System.out.println(1 / 0);//仅这里出异常的话，外层回滚，内层正常提交。
    }

    /**
     * 【错误的写法】
     */
    @Transactional
    @Override
    public void testRequiredNew2() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testRequiredNew2();

        System.out.println(1 / 0);//仅这里出异常的话，外层回滚，内层正常提交。
    }

    /**
     * 【正确的写法】
     */
    @Transactional
    @Override
    public void testRequiredNew3() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        //捕获异常，阻止异常上抛。外层事务不会监听requirednew机制的内层事务的回滚。如果内部回滚触发了外部回滚那是异常上抛引起的，不是内部回滚引起的。
        try {
            transactionNestInnerService.testRequiredNew3();
        } catch (RuntimeException e) {
            e.printStackTrace();
        }

        System.out.println(1 / 0);//仅这里出异常的话，外层回滚，内层正常提交。
    }

    /**
     * 【不推荐】【特殊业务场景下或可一用】
     */
    @Transactional
    @Override
    public void testNotSupported() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testNotSupported();

        System.out.println(1 / 0);//这里出异常的话，内层的add方法成功插入数据库，外层事务回滚。
    }

    /**
     * 【推荐】
     */
    @Transactional
    @Override
    public void testNotSupported2() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);
        System.out.println("outter向数据库中插入了User----" + user);

        //阻止异常上抛，防止内层的异常引起外层回滚
        try {
            transactionNestInnerService.testNotSupported2();
        } catch (RuntimeException e) {
            e.printStackTrace();
        }

        System.out.println(1 / 0);//这里出异常的话，内层的add方法成功插入数据库，外层事务回滚。
    }

    /**
     * 【推荐】
     */
    @Transactional
    @Override
    public void testNever() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);//回滚
        System.out.println("outter向数据库中插入了User----" + user);

        transactionNestInnerService.testNever();//抛出异常

        //上面的add方法不会落库成功。因为testNever方法会抛出异常。
    }

    /**
     * [不推荐]
     */
    @Transactional
    @Override
    public void testNever2() {
        User user = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user);//落表成功
        System.out.println("outter向数据库中插入了User----" + user);

        try {
            transactionNestInnerService.testNever();//如果这里trycatch处理的话，外层的add方法可以落表成功，但是这么做没什么实用价值。
        }catch (RuntimeException e){
            e.printStackTrace();
        }

        User user2 = new User("外层", UUID.randomUUID().toString());
        userMapper.add(user2);//落表成功
        System.out.println("outter向数据库中插入了User2----" + user2);

        //上面的add方法不会落库成功。因为testNever方法会抛出异常。
    }

}
